<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>分时函数的封装</title>
  </head>
  <body>
    <button id="btn">添加数据</button>
    <script>
      const btn = document.querySelector("#btn");
      btn.onclick = () => {
        const consumer = (item, i) => {
          const div = document.createElement("div");
          div.textContent = i;
          document.body.appendChild(div);
        };
        // 1. 自定义分块执行逻辑
        // const chunkSplitor = (task) => {
        //   const timer = setTimeout(() => {
        //     clearTimeout(timer);
        //     task((time) => time < 16);
        //   }, 30);
        // };
        // 2. 不传的话, 走默认系统自带的方法实现
        _performChunk(10000, consumer);
      };
      // 把任务分块执行, 避免卡顿
      function _performChunk(datas, consumer, chunkSplitor) {
        // 参数归一化
        if (typeof datas === "number") {
          datas = new Array(datas);
        }
        if (datas.length === 0) {
          return;
        }
        if (!chunkSplitor && globalThis.requestIdleCallback) {
          chunkSplitor = (task) => {
            requestIdleCallback((idle) => {
              task(() => idle.timeRemaining() > 0);
            });
          };
        }
        let i = 0; // 目前应该读取的任务下标
        // 执行一块任务
        function _run() {
          // 没有任务执行了
          if (i === datas.length) {
            return;
          }
          // 浏览器渲染帧间回调, 一个渲染帧大约16.6ms
          chunkSplitor((hasTime) => {
            const now = Date.now();
            // 渲染帧期间有剩余时间, 就执行逻辑
            while (hasTime(Date.now() - now) && i < datas.length) {
              const item = datas[i];
              consumer(item, i);
              i++;
            }
            // 递归调用, 下次渲染帧期间继续执行
            _run();
          });
        }
        _run();
      }
    </script>
  </body>
</html>
